package com.hjk.common.utils.web;

import com.hjk.common.constants.GlobalConstant;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.junit.Test;

import javax.imageio.ImageIO;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;

import static com.hjk.common.constants.GlobalConstant.Media.JPG;

/**
 * @author huangjunkai
 * @version 1.0
 * @date 2020/11/22 23:34
 */
@Slf4j
public class MediaUtils {

    /**
     * 生成视频的封面,截取第6帧
     *
     * @return 是否成功
     */
    public static boolean isVideoToImage(InputStream inputStream, File file) {
        try {
            FFmpegFrameGrabber ff = new FFmpegFrameGrabber(inputStream);
            ff.start();
            int length = ff.getLengthInFrames();
            int i = 0;
            Frame frame = null;
            while (i < length) {
                // 去掉前5帧，避免出现全黑的图片，依自己情况而定
                frame = ff.grabImage();
                if ((i > 5) && (frame.image != null)) {
                    break;
                }
                i++;
            }
            ff.flush();
            boolean write = ImageIO.write(new Java2DFrameConverter().getBufferedImage(frame), JPG, new BufferedOutputStream(new FileOutputStream(file)));
            ff.stop();
            return write;
        } catch (Exception e) {
            log.error("<<<<<<<<<<<<<<<<<生成视频的略缩图失败>>>>>>>>>>>>>", e);
        }
        return false;
    }

    /**
     * 生成视频的封面,截取第6帧
     *
     * @return 是否成功
     */
    public static InputStream getImageInputStream(String path) {
        try {
            FFmpegFrameGrabber ff = new FFmpegFrameGrabber(path);
            ff.start();
            int length = ff.getLengthInFrames();
            int i = 0;
            Frame frame = null;
            while (i < length) {
                // 去掉前5帧，避免出现全黑的图片，依自己情况而定
                frame = ff.grabImage();
                if ((i > 5) && (frame.image != null)) {
                    break;
                }
                i++;
            }
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            boolean write = ImageIO.write(new Java2DFrameConverter().getBufferedImage(frame), JPG, outputStream);
            ff.stop();
            return new ByteArrayInputStream(outputStream.toByteArray());
        } catch (Exception e) {
            log.error("<<<<<<<<<<<<<<<<<生成视频的略缩图失败>>>>>>>>>>>>>", e);
        }
        return null;
    }

    @Test
    public void test() throws IOException {

        InputStream inputStream = getImageInputStream("http://server.kbhjk.top:19000/file/hjk_02638590874664f91944fdfcf317ddc9.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=huangjunkai%2F20201123%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20201123T021219Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=a0d30c3e862b04e36717ec1851907b6af78b0575db644f19281910c1c8bd8364");

        IOUtils.copy(inputStream, new FileOutputStream(new File("E:\\myjar\\nav_tmp\\image_tmp\\hjk_02638590874664f91944fdfcf317ddc9.jpg")));
    }


    /**
     *
     * ffmpeg 调用可执行文件
     * -i 输入流,后面接流地址 可以对应本地地址
     * -frame:v 限定数量,后面接数字,这里只要一张图就写 1
     * -s 生成图片的像素WxH,就是宽x高
     * -f 格式化格式 image2
     * ‘-t duration (output)’当到达 duration 时，停止写输出。duration 可以是一个数字(秒)，或者使用hh:mm:ss[.xxx]形式。
     * -ss：代表要截取的的图片在视频中的位置，后面接数字，秒为单位
     * 获取视频的 某一截屏
     * @param videoPath
     * @param imagesPath
     */
    public static void getVideoImages(String videoPath, String imagesPath) {
        //获取分辨率
        String ratio = getRatio(videoPath);
        ProcessBuilder builder = new ProcessBuilder();

        String cmdFormat = "ffmpeg -i %s -y -f image2 -ss 10 -t 0.01 -s %s %s";
        String cmd = String.format(cmdFormat, videoPath, ratio, imagesPath);
        try {
            builder.command(Arrays.asList(cmd.split(GlobalConstant.Symbol.SPACE)));
            builder.redirectErrorStream(true);
            // 如果此属性为 true，则任何由通过此对象的 start() 方法启动的后续子进程生成的错误输出都将与标准输出合并，
            //因此两者均可使用 Process.getInputStream() 方法读取。这使得关联错误消息和相应的输出变得更容易
            Process start = builder.start();
            InputStream inputStream = start.getInputStream();
            log.info("执行命令 : \n<<<< {} >>>", cmd);
            log.info("执行结果: \n{}", IOUtils.toString(inputStream, StandardCharsets.UTF_8));
        } catch (Exception e) {
            log.error("执行命令错误 : \n<<<< {} >>>", cmd, e);
        }

    }

    /**
     *
     * 执行ffmpeg命令
     */
    public static void runFfmpegCmd(String cmdFormat ,Object ... cmdParam) {
        ProcessBuilder builder = new ProcessBuilder();
        String cmd = String.format(cmdFormat, cmdParam);
        try {
            builder.command(Arrays.asList(cmd.split(GlobalConstant.Symbol.SPACE)));
            builder.redirectErrorStream(true);
            // 如果此属性为 true，则任何由通过此对象的 start() 方法启动的后续子进程生成的错误输出都将与标准输出合并，
            //因此两者均可使用 Process.getInputStream() 方法读取。这使得关联错误消息和相应的输出变得更容易
            Process start = builder.start();
            InputStream inputStream = start.getInputStream();
            log.info("执行命令 : \n<<<< {} >>>", cmd);
            log.info("执行结果: \n{}", IOUtils.toString(inputStream, StandardCharsets.UTF_8));
        } catch (Exception e) {
            log.error("执行命令错误 : \n<<<< {} >>>", cmd, e);
        }

    }


    /**
     * 获取 分辨率
     *
     * @param inputStream
     * @return
     */
    @SneakyThrows
    public static String getRatio(InputStream inputStream) {
        return getRatioResult(IOUtils.toString(inputStream, StandardCharsets.UTF_8));
    }

    private static String getRatioResult(String result) {
        return Arrays.stream(result.split(GlobalConstant.Symbol.LINE))
                .filter(f -> f.contains("Stream"))
                .map(m -> Arrays.asList(m.trim().split(GlobalConstant.Symbol.COMMA)))
                .flatMap(Collection::stream)
                .filter(f2 -> f2.contains("x"))
                .skip(1)
                .findFirst()
                .map(f -> f.trim().split(GlobalConstant.Symbol.SPACE)[0])
                .orElse(null);
    }

    /**
     * 获取 分辨率
     *
     * @param path 文件路径
     * @return
     */
    @SneakyThrows
    public static String getRatio(String path) {
        String format = "ffmpeg -i %s";
        String cmd = String.format(format, path);
        ProcessBuilder builder = new ProcessBuilder();
        builder.command(Arrays.asList(cmd.split(GlobalConstant.Symbol.SPACE)));
        builder.redirectErrorStream(true);
        // 如果此属性为 true，则任何由通过此对象的 start() 方法启动的后续子进程生成的错误输出都将与标准输出合并，
        //因此两者均可使用 Process.getInputStream() 方法读取。这使得关联错误消息和相应的输出变得更容易
        Process start = builder.start();
        String result = IOUtils.toString(start.getInputStream(), StandardCharsets.UTF_8);
        return getRatioResult(result);
    }

    public static void main(String[] args) {
        getVideoImages("E:\\chrome文件下载位置\\0a390ff11a45ca0697e05cdaec0885d3.mp4", "E:\\myjar\\nav_tmp\\image_tmp\\aaab.jpg");
    }

    @Test
    public void getRatioTest() {
        System.out.println(getRatio("F:\\java\\java提高\\Netty视频教程\\视频\\netty\\36.036--Netty概述(Av76227904,P36).mp4"));

    }

    @Test
    public void runFfmpegCmdTest(){
        String path = "E:\\chrome文件下载位置\\0a390ff11a45ca0697e05cdaec0885d3.mp4";
        String resultPath = "E:\\myjar\\nav_tmp\\image_tmp\\aaab.gif";

        /**
         *
         * -v quiet:  不显示版本和处理过程等log信息
         *
         * #-s 320x180: 指定生成的gif图的宽高
         *
         * #-y : 文件存在时自动覆盖，不再询问
         *
         * -ss 00:00:02： 从第二秒开始
         *
         * -t 5:   持续5秒
         * -vframes 30 截取多少帧
         * -f gif: 指定文件格式为gif(因为输出文件名的后缀是gif,所以这个参数可以不添加)
         */
        runFfmpegCmd("ffmpeg -i %s -ss 00:00:05 -vframes 30 -y -f gif %s",path,resultPath);

    }

}
